using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;

namespace System.Drawing
{
	/// <summary>
	/// Provides methods to convert from a color space to an other.
	/// </summary>
	public sealed class ColorSpaceHelper
	{
		private ColorSpaceHelper(){}

		#region Color processing
		/// <summary>
		/// Gets the "distance" between two colors.
		/// RGB colors must be normalized (eg. values in [0.0, 1.0]).
		/// </summary>
		/// <param name="r1">First color red component.</param>
		/// <param name="g1">First color green component.</param>
		/// <param name="b1">First color blue component.</param>
		/// <param name="r2">Second color red component.</param>
		/// <param name="g2">Second color green component.</param>
		/// <param name="b2">Second color blue component.</param>
		public static double GetColorDistance(double r1, double g1, double b1, double r2, double g2, double b2)
		{
			double a = r2 - r1;
			double b = g2 - g1;
			double c = b2 - b1;
    
			return Math.Sqrt(a*a + b*b + c*c);
		}
    
	
		/// <summary>
		/// Gets the "distance" between two colors.
		/// RGB colors must be normalized (eg. values in [0.0, 1.0]).
		/// </summary>
		/// <param name="color1">First color [r,g,b]</param>
		/// <param name="color2">Second color [r,g,b]</param>
		public static double GetColorDistance(double[] color1, double[] color2)
		{
			return GetColorDistance(color1[0], color1[1], color1[2], color2[0], color2[1], color2[2]);
		}
  
	
		/// <summary>
		/// Gets the "distance" between two colors.
		/// </summary>
		/// <param name="color1">First color.</param>
		/// <param name="color2">Second color.</param>
		public static double GetColorDistance(Color c1, Color c2)
		{
			double[] rgb1 = new double[]{
											(double)c1.R/255.0,
											(double)c1.G/255.0,
											(double)c1.B/255.0
										};

			double[] rgb2 = new double[]{
											(double)c2.R/255.0,
											(double)c2.G/255.0,
											(double)c2.B/255.0
										};

			return GetColorDistance(rgb1[0], rgb1[1], rgb1[2], rgb2[0], rgb2[1], rgb2[2]);
		}

		
		#endregion

		#region Light Spectrum processing
		/// <summary>
		/// Gets visible colors (color wheel).
		/// </summary>
		/// <param name="alpha">
		/// The alpha value used for each colors.
		/// </param>
		public static Color[] GetWheelColors(int alpha)
		{
			Color temp;
			int colorCount = 6*256;
			Color[] colors = new Color[colorCount];

			for(int i=0 ; i<colorCount; i++)
			{
				temp = HSBtoColor((int)((double)(i*255.0)/colorCount), 255, 255);
				colors[i] = Color.FromArgb(alpha, temp.R, temp.G, temp.B);
			}

			return colors;
		}


		/// <summary>
		/// Gets visible spectrum colors.
		/// </summary>
		/// <param name="alpha">The alpha value used for each colors.</param>
		public static Color[] GetSpectrumColors(int alpha)
		{
			Color[] colors = new Color[256*6];
			//for(int i=127; i<256; i++) colors[i-127] = Color.FromArgb(alpha, i, 0, 0);
			for(int i=0; i<256; i++) colors[i] = Color.FromArgb(alpha, 255, i, 0);
			for(int i=0; i<256; i++) colors[i+(256)] = Color.FromArgb(alpha, 255-i, 255, 0);
			for(int i=0; i<256; i++) colors[i+(256*2)] = Color.FromArgb(alpha, 0, 255, i);
			for(int i=0; i<256; i++) colors[i+(256*3)] = Color.FromArgb(alpha, 0, 255-i, 255);
			for(int i=0; i<256; i++) colors[i+(256*4)] = Color.FromArgb(alpha, i, 0, 255);
			for(int i=0; i<256; i++) colors[i+(256*5)] = Color.FromArgb(alpha, 255, 0, 255-i);
			//for(int i=0; i<128; i++) colors[i+(128+256*6)] = Color.FromArgb(alpha, 255-i, 0, 0);

			return colors;
		}

		
		/// <summary>
		/// Gets visible spectrum colors.
		/// </summary>
		public static Color[] GetSpectrumColors()
		{
			return GetSpectrumColors(255);
		}

		
		#endregion

		#region Hexa convert
		/// <summary>
		/// Gets the int equivalent for a hexadecimal value.
		/// </summary>
		private static int GetIntFromHex(string strHex)
		{
			switch(strHex)
			{
				case("A"):
				{
					return 10;
				}
				case("B"):
				{
					return 11;
				}
				case("C"):
				{
					return 12;
				}
				case("D"):
				{
					return 13;
				}
				case("E"):
				{
					return 14;
				}
				case("F"):
				{
					return 15;
				}
				default:
				{
					return Int32.Parse(strHex);
				}
			}
		}


		/// <summary>
		/// Converts a Hex color to a .net Color.
		/// </summary>
		/// <param name="hexColor">The desired hexadecimal color to convert.</param>
		public static Color HexToColor(string hexColor)
		{
			string r,g,b;

			if(hexColor != String.Empty)
			{
				hexColor = hexColor.Trim();
				if(hexColor[0] == '#') hexColor = hexColor.Substring(1, hexColor.Length-1);
				
				r = hexColor.Substring(0,2);
				g = hexColor.Substring(2,2);
				b = hexColor.Substring(4,2);

				r = Convert.ToString(16 * GetIntFromHex(r.Substring(0,1)) + GetIntFromHex(r.Substring(1,1)));
				g = Convert.ToString(16 * GetIntFromHex(g.Substring(0,1)) + GetIntFromHex(g.Substring(1,1)));
				b = Convert.ToString(16 * GetIntFromHex(b.Substring(0,1)) + GetIntFromHex(b.Substring(1,1)));

				return Color.FromArgb(Convert.ToInt32(r), Convert.ToInt32(g), Convert.ToInt32(b));
			}

			return Color.Empty;
		}
		
		/// <summary>
		/// Converts a RGB color format to an hexadecimal color.
		/// </summary>
		/// <param name="r">The Red value.</param>
		/// <param name="g">The Green value.</param>
		/// <param name="b">The Blue value.</param>
		public static string RGBToHex(int r, int g, int b)
		{
			return String.Format("#{0:x2}{1:x2}{2:x2}", (int)r, (int)g, (int)b);
		}

		
		/// <summary>
		/// Converts a RGB color format to an hexadecimal color.
		/// </summary>
		/// <param name="r">The color to convert.</param>
		public static string RGBToHex(Color c)
		{
			return RGBToHex(c.R, c.G, c.B);
		}

		#endregion

		#region HSB convert
		/// <summary>
		/// Converts HSB to RGB.
		/// </summary>
		/// <param name="hsv">The HSB structure to convert.</param>
		public static RGB HSBtoRGB(HSB hsb) 
		{
			double r = 0;
			double g = 0;
			double b = 0;

			if(hsb.Saturation == 0) 
			{
				r = g = b = hsb.Brightness;
			} 
			else 
			{
				// the color wheel consists of 6 sectors. Figure out which sector you're in.
				double sectorPos = hsb.Hue / 60.0;
				int sectorNumber = (int)(Math.Floor(sectorPos));
				// get the fractional part of the sector
				double fractionalSector = sectorPos - sectorNumber;

				// calculate values for the three axes of the color. 
				double p = hsb.Brightness * (1.0 - hsb.Saturation);
				double q = hsb.Brightness * (1.0 - (hsb.Saturation * fractionalSector));
				double t = hsb.Brightness * (1.0 - (hsb.Saturation * (1 - fractionalSector)));

				// assign the fractional colors to r, g, and b based on the sector the angle is in.
				switch (sectorNumber) 
				{
					case 0:
						r = hsb.Brightness;
						g = t;
						b = p;
						break;
					case 1:
						r = q;
						g = hsb.Brightness;
						b = p;
						break;
					case 2:
						r = p;
						g = hsb.Brightness;
						b = t;
						break;
					case 3:
						r = p;
						g = q;
						b = hsb.Brightness;
						break;
					case 4:
						r = t;
						g = p;
						b = hsb.Brightness;
						break;
					case 5:
						r = hsb.Brightness;
						g = p;
						b = q;
						break;
                    case 6:
                        r = hsb.Brightness;
                        g = t;
                        b = p;
                        break;
				}
			}
			
			return new RGB(
				Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", r*255.0)) ),
				Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", g*255.0)) ),
				Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", b*255.0)) )
				);
		}

		/// <summary>
		/// Converts HSB to RGB.
		/// </summary>
		/// <param name="H">Hue value.</param>
		/// <param name="S">Saturation value.</param>
		/// <param name="V">Brigthness value.</param>
		public static RGB HSBtoRGB(double hue, double sularation, double brightness) 
		{
			return HSBtoRGB(new HSB(hue, sularation, brightness));
		}


		/// <summary>
		/// Converts HSB to Color.
		/// </summary>
		/// <param name="hsv">the HSB structure to convert.</param>
		public static Color HSBtoColor(HSB hsb) 
		{
			RGB rgb = HSBtoRGB(hsb);

			return Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue);
		}
	
		/// <summary> 
		/// Converts HSB to a .net Color.
		/// </summary>
		/// <param name="h">Hue value (must be between 0 and 360).</param>
		/// <param name="s">Saturation value (must be between 0 and 1).</param>
		/// <param name="b">Brightness value (must be between 0 and 1).</param>
		public static Color HSBtoColor(double h, double s, double b) 
		{
			return HSBtoColor( new HSB(h,s,b) );
		} 
  
		/// <summary>
		/// Converts HSB to Color.
		/// </summary>
		/// <param name="h">Hue value.</param>
		/// <param name="s">Saturation value.</param>
		/// <param name="b">Brightness value.</param>
		public static Color HSBtoColor(int h,  int s,  int b) 
		{
			double hue=0,sat=0,val=0;

			// Scale Hue to be between 0 and 360. Saturation and value scale to be between 0 and 1.
			if(h>360 || s>1 || b>1)
			{
				hue = ((double)h / 255.0 * 360.0) % 360.0;
				sat = (double)s / 255.0;
				val = (double)b / 255.0;
			}

			return HSBtoColor(new HSB(hue, sat, val));
		}

		
		/// <summary>
		/// Converts HSB to HSL.
		/// </summary>
		public static HSL HSBtoHSL(double h, double s, double b)
		{
			RGB rgb = HSBtoRGB( new HSB(h, s, b) );

			return RGBtoHSL(rgb.Red, rgb.Green, rgb.Blue);
		}

		#endregion

		#region HSL convert
		/// <summary>
		/// Converts HSL to RGB.
		/// </summary>
		/// <param name="h">Hue, must be in [0, 360].</param>
		/// <param name="s">Saturation, must be in [0, 1].</param>
		/// <param name="l">Luminance, must be in [0, 1].</param>
		public static RGB HSLtoRGB(double h, double s, double l) 
		{
			if(s == 0)
			{
				// achromatic color (gray scale)
				return new RGB(
					Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", l*255.0)) ), 
					Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", l*255.0)) ), 
					Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", l*255.0)) )
					);
			}
			else
			{
				double q = (l<0.5)?(l * (1.0+s)):(l+s - (l*s));
				double p = (2.0 * l) - q;

				double Hk = h/360.0;
				double[] T = new double[3];
				T[0] = Hk + (1.0/3.0);	// Tr
				T[1] = Hk;				// Tb
				T[2] = Hk - (1.0/3.0);	// Tg

				for(int i=0; i<3; i++)
				{
					if(T[i] < 0) T[i] += 1.0;
					if(T[i] > 1) T[i] -= 1.0;

					if((T[i]*6) < 1)
					{
						T[i] = p + ((q-p)*6.0*T[i]);
					}
					else if((T[i]*2.0) < 1) //(1.0/6.0)<=T[i] && T[i]<0.5
					{
						T[i] = q;
					}
					else if((T[i]*3.0) < 2) // 0.5<=T[i] && T[i]<(2.0/3.0)
					{
						T[i] = p + (q-p) * ((2.0/3.0) - T[i]) * 6.0;
					}
					else T[i] = p;
				}

				return new RGB(
					Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", T[0]*255.0)) ), 
					Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", T[1]*255.0)) ), 
					Convert.ToInt32( Double.Parse(String.Format("{0:0.00}", T[2]*255.0)) )
					);
			}
		}
	
		/// <summary>
		/// Converts HSL to RGB.
		/// </summary>
		/// <param name="hsl">The HSL structure to convert.</param>
		public static RGB HSLtoRGB(HSL hsl) 
		{
			return HSLtoRGB(hsl.Hue, hsl.Saturation, hsl.Luminance);
		}

		
		/// <summary>
		/// Converts HSL to .net Color.
		/// </summary>
		/// <param name="hsl">The HSL structure to convert.</param>
		public static Color HSLtoColor(double h, double s, double l) 
		{
			RGB rgb = HSLtoRGB(h, s, l);

			return Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue);
		}

		/// <summary>
		/// Converts HSL to .net Color.
		/// </summary>
		/// <param name="hsl">The HSL structure to convert.</param>
		public static Color HSLtoColor(HSL hsl) 
		{
			return HSLtoColor(hsl.Hue, hsl.Saturation, hsl.Luminance);
		}

		
		/// <summary>
		/// Converts HSL to HSB.
		/// </summary>
		public static HSB HSLtoHSB(double h, double s, double l)
		{
			RGB rgb = HSLtoRGB(h, s, l);

			return RGBtoHSB(rgb.Red, rgb.Green, rgb.Blue);
		}

		#endregion

		#region RGB convert
		/// <summary> 
		/// Converts RGB to HSL.
		/// </summary>
		/// <param name="red">Red value, must be in [0,255].</param>
		/// <param name="green">Green value, must be in [0,255].</param>
		/// <param name="blue">Blue value, must be in [0,255].</param>
		public static HSL RGBtoHSL(int red, int green, int blue) 
		{
			double h=0, s=0, l=0;

			// normalizes red-green-blue values
			double nRed = (double)red/255.0;
			double nGreen = (double)green/255.0;
			double nBlue = (double)blue/255.0;

			double max = Math.Max(nRed, Math.Max(nGreen, nBlue));
			double min = Math.Min(nRed, Math.Min(nGreen, nBlue));

			// hue
			if(max == min)
			{
				h = 0; // undefined
			}
			else if(max==nRed && nGreen>=nBlue)
			{
				h = 60.0*(nGreen-nBlue)/(max-min);
			}
			else if(max==nRed && nGreen<nBlue)
			{
				h = 60.0*(nGreen-nBlue)/(max-min) + 360.0;
			}
			else if(max==nGreen)
			{
				h = 60.0*(nBlue-nRed)/(max-min) + 120.0;
			}
			else if(max==nBlue)
			{
				h = 60.0*(nRed-nGreen)/(max-min) + 240.0;
			}

			// luminance
			l = (max+min)/2.0;

			// saturation
			if(l == 0 || max == min)
			{
				s = 0;
			}
			else if(0<l && l<=0.5)
			{
				s = (max-min)/(max+min);
			}
			else if(l>0.5)
			{
				s = (max-min)/(2 - (max+min)); //(max-min > 0)?
			}

			return new HSL(
				Double.Parse(String.Format("{0:0.##}", h)),
				Double.Parse(String.Format("{0:0.##}", s)),
				Double.Parse(String.Format("{0:0.##}", l))
				); 
		} 

		/// <summary> 
		/// Converts RGB to HSL.
		/// </summary>
		public static HSL RGBtoHSL(RGB rgb)
		{
			return RGBtoHSL(rgb.Red, rgb.Green, rgb.Blue);
		}

		/// <summary> 
		/// Converts Color to HSL.
		/// </summary>
		public static HSL RGBtoHSL(Color c)
		{
			return RGBtoHSL(c.R, c.G, c.B);
		}

		
		/// <summary> 
		/// Converts RGB to HSB.
		/// </summary> 
		public static HSB RGBtoHSB(int red, int green, int blue) 
		{ 
			double r = ((double)red/255.0);
			double g = ((double)green/255.0);
			double b = ((double)blue/255.0);

			double max = Math.Max(r, Math.Max(g, b));
			double min = Math.Min(r, Math.Min(g, b));

			double h = 0.0;
			if(max==r && g>=b)
			{
				if(max-min == 0) h = 0.0;
				else h = 60 * (g-b)/(max-min);
			}
			else if(max==r && g < b)
			{
				h = 60 * (g-b)/(max-min) + 360;
			}
			else if(max == g)
			{
				h = 60 * (b-r)/(max-min) + 120;
			}
			else if(max == b)
			{
				h = 60 * (r-g)/(max-min) + 240;
			}

			double s = (max == 0)? 0.0 : (1.0-((double)min/(double)max));

			return new HSB(h, s, (double)max);
		} 

		/// <summary> 
		/// Converts RGB to HSB.
		/// </summary> 
		public static HSB RGBtoHSB(RGB rgb) 
		{ 
			return RGBtoHSB(rgb.Red, rgb.Green, rgb.Blue);
		} 
		
		/// <summary> 
		/// Converts RGB to HSB.
		/// </summary> 
		public static HSB RGBtoHSB(Color c)
		{ 
			return RGBtoHSB(c.R, c.G, c.B);
        }




        #endregion

    }
}
